#pragma once

#include <AVUIDispatcherObject.h>
#include <AVUIItemStructMap.h>
#include <AVUIDependencyProperty.h>
#include <AVUIDependentList.h>
#include <AVUIUncommonField.h>
#include <AVUIExpression.h>
#include <AVUIEffectiveValueEntry.h>
#include <AVUILocalValueEntry.h>

// [REVIEWED 08/02/2008 -- GHERMANN]
namespace AVUI {

class Expression;
class EventRoute;
class Dispatcher;
class DependencyPropertyChangedEventArgs;
class DependencyPropertyKey;
class LocalValueEnumerator;
class PropertyMetadata;

/// <summary>Represents an object that participates in the dependency property system.</summary> 
class DependencyObject : public DispatcherObject
{
    class EffectiveValue;
    friend class Freezable;
public:

    DECLARE_ELEMENT(DependencyObject, DispatcherObject);

#pragma region Public Methods

    /// <summary>Initializes a new instance of the <see cref="T:System.Windows.DependencyObject" /> class. </summary>
    DependencyObject();

    virtual ~DependencyObject();

    /// <summary>Clears the local value of a property. The property to be cleared is specified by a <see cref="T:System.Windows.DependencyProperty" /> identifier. </summary>
    /// <param name="pDP">The dependency property to be cleared, identified by a <see cref="T:System.Windows.DependencyProperty" /> object reference.</param>
    /// <exception cref="T:System.InvalidOperationException">Attempted to call <see cref="M:System.Windows.DependencyObject.ClearValue(System.Windows.DependencyProperty)" /> on a sealed <see cref="T:System.Windows.DependencyObject" />.</exception>
    void ClearValue(DependencyProperty* pDP);

    /// <summary>Clears the local value of a read-only property. The property to be cleared is specified by a <see cref="T:System.Windows.DependencyPropertyKey" />. </summary>
    /// <param name="pKey">The key for the dependency property to be cleared.</param>
    /// <exception cref="T:System.InvalidOperationException">Attempted to call <see cref="M:System.Windows.DependencyObject.ClearValue(System.Windows.DependencyProperty)" /> on a sealed <see cref="T:System.Windows.DependencyObject" />.</exception>
    void ClearValue(DependencyPropertyKey* pKey);

    /// <summary>Coerces the value of the specified dependency property. This is accomplished by invoking any <see cref="T:System.Windows.CoerceValueCallback" /> function specified in property metadata for the dependency property as it exists on the calling <see cref="T:System.Windows.DependencyObject" />.</summary>
    /// <param name="pDP">The identifier for the dependency property to coerce.</param>
    /// <exception cref="T:System.InvalidOperationException">The specified <paramref name="pDP" /> or its value were invalid or do not exist.</exception>
    void CoerceValue(DependencyProperty* pDP);

    /// <summary>Creates a specialized enumerator for determining which dependency properties have locally set values on this <see cref="T:System.Windows.DependencyObject" />. </summary>
    /// <returns>A specialized local value enumerator.</returns>
    TRefCountedPtr<LocalValueEnumerator> GetLocalValueEnumerator();

    /// <summary>Returns the current effective value of a dependency property on this instance of a <see cref="T:System.Windows.DependencyObject" />. </summary>
    /// <returns>Returns the current effective value.</returns>
    /// <param name="pDP">The <see cref="T:System.Windows.DependencyProperty" /> identifier of the property to retrieve the value for.</param>
    /// <exception cref="T:System.InvalidOperationException">The specified <paramref name="pDP" /> or its value was invalid, or the specified <paramref name="pDP" /> does not exist.</exception>
    ObjectPtr GetValue(DependencyProperty* pDP);

    /// <summary>Returns the current effective value of a dependency property on this instance of a <see cref="T:System.Windows.DependencyObject" />. </summary>
    /// <returns>Returns the current effective value.</returns>
    /// <param name="pDP">The <see cref="T:System.Windows.DependencyProperty" /> identifier of the property to retrieve the value for.</param>
    /// <exception cref="T:System.InvalidOperationException">The specified <paramref name="pDP" /> or its value was invalid, or the specified <paramref name="pDP" /> does not exist.</exception>
    void InvalidateProperty(DependencyProperty* pDP);

    /// <summary>Returns the local value of a dependency property, if it exists. </summary>
    /// <returns>Returns the local value, or returns the sentinel value <see cref="F:System.Windows.DependencyProperty.UnsetValue" /> if no local value is set.</returns>
    /// <param name="pDP">The <see cref="T:System.Windows.DependencyProperty" /> identifier of the property to retrieve the value for.</param>
    Object* ReadLocalValue(DependencyProperty* pDP);

    /// <summary>Sets the local value of a dependency property, specified by its dependency property identifier. </summary>
    /// <param name="pDP">The identifier of the dependency property to set.</param>
    /// <param name="pValue">The new local value.</param>
    /// <exception cref="T:System.InvalidOperationException">Attempted to modify a read-only dependency property, or a property on a sealed <see cref="T:System.Windows.DependencyObject" />.</exception>
    /// <exception cref="T:System.ArgumentException">
    /// <paramref name="pValue" /> was not the correct type as registered for the <paramref name="pDP" /> property.</exception>
    void SetValue(DependencyProperty* pDP, Object* pValue);

    /// <summary>Sets the local value of a read-only dependency property, specified by the <see cref="T:System.Windows.DependencyPropertyKey" /> identifier of the dependency property. </summary>
    /// <param name="key">The <see cref="T:System.Windows.DependencyPropertyKey" /> identifier of the property to set.</param>
    /// <param name="pValue">The new local value.</param>
    void SetValue(DependencyPropertyKey* pKey, Object* pValue);

#pragma endregion Public Methods
    
#pragma region Public Properties

    /// <summary>Gets a value that declares whether this instance is currently read-only.</summary>
    /// <returns>true if this instance is sealed; otherwise, false.</returns>
    DECLARE_READONLY_PROPERTY(IsSealed, bool, false);
    bool get_IsSealed() { return get_DO_Sealed(); }

#pragma endregion Public Properties

protected:
#pragma region Protected Methods

    /// <summary>Invoked whenever the effective value of any dependency property on this <see cref="T:System.Windows.DependencyObject" /> has been updated. The specific dependency property that changed is reported in the event data. </summary>
    /// <param name="e">Event data that will contain the dependency property identifier of interest, the property metadata for the type, and old and new values.</param>
    virtual void OnPropertyChanged(DependencyPropertyChangedEventArgs* e);

    /// <summary>Returns a value that indicates whether serialization processes should serialize the value for the provided dependency property.</summary>
    /// <returns>true if the dependency property that is supplied should be value-serialized; otherwise, false.</returns>
    /// <param name="pDP">The identifier for the dependency property that should be serialized.</param>
    virtual bool ShouldSerializeProperty(DependencyProperty* pDP);

#pragma endregion Protected Methods

public:

#pragma region Internal Methods

    virtual void AddInheritanceContext(DependencyObject* pContext, DependencyProperty* pProperty) { }
    void BeginPropertyInitialization() { set_IsInPropertyInitialization(true); }
    static void ChangeExpressionSources(Expression* pExpr, DependencyObject* pDO, DependencyProperty* pDP, ItemStructList<TRefCountedPtr<DependencySource> >* pNewSources);
    bool ContainsValue(DependencyProperty* pDP);
    void EndPropertyInitialization();
    virtual void EvaluateAnimatedValueCore(DependencyProperty* pDP, PropertyMetadata* pMetadata, EffectiveValueEntry* pNewEntry) { }
    virtual void EvaluateBaseValueCore(DependencyProperty* pDP, PropertyMetadata* pMetadata, EffectiveValueEntry* pNewEntry) { };
    EffectiveValueEntry GetValueEntry(const EntryIndex& entryIndex, DependencyProperty* pDP, PropertyMetadata* pMetadata, RequestFlags::Enum requests);
    BaseValueSource::Enum GetValueSource(DependencyProperty* pDP, PropertyMetadata* pMetadata, bool* pHasModifiers);
    BaseValueSource::Enum GetValueSource(DependencyProperty* pDP, PropertyMetadata* pMetadata, bool* pHasModifiers, bool* pIsExpression, bool* pIsAnimated, bool* pIsCoerced);
    bool HasAnyExpression();
    bool HasExpression(EntryIndex entryIndex, DependencyProperty* pDP);
    void InvalidateSubProperty(DependencyProperty* pDP);
    EntryIndex LookupEntry(int targetIndex);
    void NotifyPropertyChange(DependencyPropertyChangedEventArgs*);
    void NotifySubPropertyChange(DependencyProperty* pDP);
    void OnInheritanceContextChanged(EventArgs* pArgs);
    virtual void OnInheritanceContextChangedCore(EventArgs* pArgs) { };
    bool ProvideSelfAsInheritanceContext(Object* pValue, DependencyProperty* pDP);
    bool ProvideSelfAsInheritanceContext(DependencyObject* pValue, DependencyProperty* pDP);
    Object* ReadLocalValueEntry(EntryIndex entryIndex, DependencyProperty* pDP, bool allowDeferredReferences);
    virtual void RemoveInheritanceContext(DependencyObject*, DependencyProperty* pDP) { }
    bool RemoveSelfAsInheritanceContext(Object* pValue, DependencyProperty* pDP);
    bool RemoveSelfAsInheritanceContext(DependencyObject* pDOValue, DependencyProperty* pDP);
    virtual void Seal();
    void SetAnimatedValue(EntryIndex entryIndex, DependencyProperty* pDP, PropertyMetadata* pMetadata, Object* pValue, Object* pBaseValue);
    void SetDeferredValue(DependencyProperty* pDP, DeferredReference* pDeferredReference);
    void SetEffectiveValue(EntryIndex entryIndex, DependencyProperty* pDP, PropertyMetadata* pMetadata, EffectiveValueEntry newEntry, EffectiveValueEntry oldEntry);
    void SetEffectiveValue(EntryIndex entryIndex, DependencyProperty* pDP, int targetIndex, PropertyMetadata* pMetadata, Object* pValue, BaseValueSource::Enum valueSource);
    void SetIsSelfInheritanceParent();
    void SetMutableDefaultValue(DependencyProperty* pDP, Object* pValue);
    void SetValueInternal(DependencyProperty* pDP, Object* pValue);
    virtual bool ShouldProvideInheritanceContext(DependencyObject* pTarget, DependencyProperty* pProperty) { return true; };
    void SynchronizeInheritanceParent(DependencyObject* pParent);
    void UnsetEffectiveValue(EntryIndex entryIndex, DependencyProperty* pDP, PropertyMetadata* pMetadata);
    UpdateResult::Enum UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty* pDP, PropertyMetadata* pMetadata, EffectiveValueEntry oldEntry, EffectiveValueEntry* pNewEntry, bool coerceWithDeferredReference, OperationType::Enum operationType);
    static void UpdateSourceDependentLists(DependencyObject* pDO, DependencyProperty* pDP, ItemStructList<TRefCountedPtr<DependencySource> >* pSources, Expression* pExpression, bool isAdd);
    static void ValidateSources(DependencyObject* pDO, ItemStructList<TRefCountedPtr<DependencySource> >* pSources, Expression* pExpr);

#pragma endregion Internal Methods

#pragma region Internal Properties

    bool get_Animatable_IsResourceInvalidationNecessary() { return ((m_packedData & 0x40000000) != 0); }
    void set_Animatable_IsResourceInvalidationNecessary(bool value)
    {
        if (value)
        {
            m_packedData |= 0x40000000;
        }
        else
        {
            m_packedData &= 0xbfffffff;
        }
    }

    bool get_CanBeInheritanceContext() { return ((m_packedData & 0x200000) != 0); }
    void set_CanBeInheritanceContext(bool value)
    {
        if (value)
        {
            m_packedData |= 0x200000;
        }
        else
        {
            m_packedData &= 0xffdfffff;
        }
    }

    EffectiveValueEntry* get_EffectiveValues() { return m_rgEffectiveValues; }
    int get_EffectiveValuesCount() { return (m_packedData & 0x3ff); }
    void set_EffectiveValuesCount(unsigned int value) {  m_packedData = (m_packedData & 0xfffffc00) | (value & 0x3ff); }

    virtual int get_EffectiveValuesInitialSize() { return 2; }

    bool get_Freezable_Frozen() { return get_DO_Sealed(); }
    void set_Freezable_Frozen(bool value) { set_DO_Sealed(value); }

    bool get_Freezable_HasMultipleInheritanceContexts() { return ((m_packedData & 0x2000000) != 0); }
    void set_Freezable_HasMultipleInheritanceContexts(bool value)
    {
        if (value)
        {
            m_packedData |= 0x2000000;
        }
        else
        {
            m_packedData &= 0xfdffffff;
        }
    }

    bool get_Freezable_UsingContextList() { return ((m_packedData & 0x8000000) != 0); }
    void set_Freezable_UsingContextList(bool value)
    {
        if (value)
        {
            m_packedData |= 0x8000000;
        }
        else
        {
            m_packedData &= 0xf7ffffff;
        }
    }

    bool get_Freezable_UsingHandlerList() { return ((m_packedData & 0x4000000) != 0); }
    void set_Freezable_UsingHandlerList(bool value)
    {
        if (value)
        {
            m_packedData |= 0x4000000;
        }
        else
        {
            m_packedData &= 0xfbffffff;
        }
    }

    bool get_Freezable_UsingSingletonContext() { return ((m_packedData & 0x20000000) != 0); }
    void set_Freezable_UsingSingletonContext(bool value)
    {
        if (value)
        {
            m_packedData |= 0x20000000;
        }
        else
        {
            m_packedData &= 0xdfffffff;
        }
    }

    bool get_Freezable_UsingSingletonHandler() { return ((m_packedData & 0x10000000) != 0); }
    void set_Freezable_UsingSingletonHandler(bool value)
    {
        if (value)
        {
            m_packedData |= 0x10000000;
        }
        else
        {
            m_packedData &= 0xefffffff;
        }
    }

    virtual bool get_HasMultipleInheritanceContexts() { return false; }

    bool get_IAnimatable_HasAnimatedProperties() { return ((m_packedData & 0x80000000) != 0); }
    void set_IAnimatable_HasAnimatedProperties(bool value)
    {
        if (value)
        {
            m_packedData |= 0x80000000;
        }
        else
        {
            m_packedData &= 0x7fffffff;
        }
    }

    unsigned int get_InheritableEffectiveValuesCount() { return ((m_packedData >> 10) & 0x1ff); }
    void set_InheritableEffectiveValuesCount(unsigned int value) {  m_packedData = ((unsigned int) ((value & 0x1ff) << 10)) | (m_packedData & 0xfff803ff); }

    virtual DependencyObject* get_InheritanceContext() { return NULL; }
    DependencyObject* get_InheritanceParent()
    {
        if ((m_packedData & 0x3e100000) == 0)
        {
            return object_cast<DependencyObject>(m_pContextStorage);
        }
        return NULL;
    }

    bool get_IsInheritanceContextSealed() { return ((m_packedData & 0x1000000) != 0); }
    void set_IsInheritanceContextSealed(bool value)
    {
        if (value)
        {
            m_packedData |= 0x1000000;
        }
        else
        {
            m_packedData &= 0xfeffffff;
        }
    }

    bool get_IsSelfInheritanceParent() { return ((m_packedData & 0x100000) != 0); }

#pragma endregion Internal Properties

#pragma region Internal Events

    IMPLEMENT_EVENT(InheritanceContextChanged, EventArgs);

#pragma endregion Internal Events

#pragma region WPF Deviation -- Private Methods

    void ClearEffectiveValues();

#pragma endregion WPF Deviation -- Private Methods

private:

#pragma region Private Methods

    EntryIndex CheckEntryIndex(EntryIndex entryIndex, int targetIndex);
    void ClearValueCommon(EntryIndex entryIndex, DependencyProperty* pDP, PropertyMetadata* pMetadata);
    bool Equals(DependencyProperty* pDP, Object* pObject1, Object* pObject2);
    EffectiveValueEntry EvaluateEffectiveValue(EntryIndex entryIndex, DependencyProperty* pDP, PropertyMetadata* pMetadata, EffectiveValueEntry oldEntry, EffectiveValueEntry newEntry, OperationType::Enum operationType);
    EffectiveValueEntry EvaluateExpression(EntryIndex entryIndex, DependencyProperty* pDP, Expression* pExpression, PropertyMetadata* pMetadata, EffectiveValueEntry oldEntry, EffectiveValueEntry newEntry);
    EffectiveValueEntry GetEffectiveValue(const EntryIndex& entryIndex, DependencyProperty* pDP, RequestFlags::Enum requests);
    static TRefCountedPtr<Expression> GetExpression(DependencyObject* pDO, DependencyProperty* pDP, PropertyMetadata* pMetadata);
    void Initialize();
    void InsertEntry(const EffectiveValueEntry& entry, unsigned int entryIndex);
    void MergeInheritableProperties(DependencyObject* pInheritanceParent);
    void RemoveEntry(unsigned int entryIndex);
    EffectiveValueEntry SetCoercedValue(EntryIndex entryIndex, DependencyProperty* pDP, PropertyMetadata* pMetadata, Object* pValue, Object* pBaseValue);
    void SetExpressionValue(EntryIndex entryIndex, Object* pValue, Object* pBaseValue);
    void SetInheritanceParent(DependencyObject* pNewParent);
    PropertyMetadata* SetupPropertyChange(DependencyProperty* pDP);
    PropertyMetadata* SetupPropertyChange(DependencyPropertyKey* pKey, DependencyProperty** ppDP);
    void SetValueCommon(DependencyProperty* pDP, Object* pValue, PropertyMetadata* pMetadata, bool coerceWithDeferredReference, OperationType::Enum operationType, bool isInternal);
    void UnsetAnimatedValue(EntryIndex entryIndex);
 
#pragma endregion Private Methods
 
#pragma region Private Properties

    bool get_CanModifyEffectiveValues() { return ((m_packedData & 0x80000) != 0); }
    void set_CanModifyEffectiveValues(bool value)
    {
        if (value)
        {
            m_packedData |= 0x80000;
        }
        else
        {
            m_packedData &= 0xfff7ffff;
        }
    }

    bool get_DO_Sealed() { return ((m_packedData & 0x400000) != 0); }
    void set_DO_Sealed(bool value)
    {
        if (value)
        {
            m_packedData |= 0x400000;
        }
        else
        {
            m_packedData &= 0xffbfffff;
        }
    }

    bool get_IsInPropertyInitialization() { return ((m_packedData & 0x800000) != 0); }
    void set_IsInPropertyInitialization(bool value)
    {
        if (value)
        {
            m_packedData |= 0x800000;
        }
        else
        {
            m_packedData &= 0xff7fffff;
        }
    }

#pragma endregion Private Properties

#pragma region Private Fields

    TWeakPtr<Object> m_pContextStorage;
    EffectiveValueEntry* m_rgEffectiveValues;
    unsigned short m_effectiveValuesCapacity;
    unsigned int m_packedData;
    static Object ExpressionInAlternativeStore;
    static UncommonField<Object> DependentListMapField;
    static const int NestedOperationMaximum  = 0x99;

#pragma endregion Private Fields
};


typedef TRefCountedPtr<DependencyObject> DependencyObjectPtr;
typedef TWeakPtr<DependencyObject> DependencyObjectWeakPtr;



}; // namespace AVUI
